package cn.apienvrypt.util;

import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * AES 是一种可逆加密算法，对用户的敏感信息加密处理 对原始数据进行AES加密后，在进行Base64编码转化；
 */
public class AESUtil {
	private static final char[] HEX_CHAR = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
	private static AESUtil instance = null;
	/**
	 * 加密用的Key 可以用26个字母和数字组成 此处使用AES-128-CBC加密模式，key需要为16位。
	 */
	private String appKey = "V75o98eDSioFCqDk";
	private String appKe2 = "V75o98eDSioFCqDg";
	/**
	 * 默认向量，向量不要变
	 */
	private String appIV = "0392039203920300";

	public static AESUtil getInstance() {
		if(instance == null) {
			instance = new AESUtil();
		}
		return instance;
	}

	private static String Encrypt(String encData, String secretKey, String vector) throws Exception {

		if(secretKey == null) {
			return null;
		}
		if(secretKey.length() != 16) {
			return null;
		}
		Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
		byte[] raw = secretKey.getBytes();
		SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
		IvParameterSpec iv = new IvParameterSpec(vector.getBytes());// 使用CBC模式，需要一个向量iv，可增加加密算法的强度
		cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
		byte[] encrypted = cipher.doFinal(encData.getBytes("utf-8"));
		return Base64Util.encode(encrypted);// 此处使用BASE64做转码。
	}

	@SuppressWarnings("unused")
	private static String encodeBytes(byte[] bytes) {
		StringBuffer strBuf = new StringBuffer();

		for(int i = 0; i < bytes.length; i++) {
			strBuf.append((char) (((bytes[i] >> 4) & 0xF) + ((int) 'a')));
			strBuf.append((char) (((bytes[i]) & 0xF) + ((int) 'a')));
		}

		return strBuf.toString();
	}

	public static void main(String[] args) throws Exception {
		// 需要加密的字串
		String cSrc = "{\"name\":\"李发家\",\"age\":11}";

		// 加密
		long lStart = System.currentTimeMillis();
		String enString = AESUtil.getInstance()
			.encrypt(cSrc);
		System.out.println("使用key:" + getInstance().appKey + "加密后的字串是：" + enString);
		long lUseTime = System.currentTimeMillis() - lStart;
		System.out.println("加密耗时：" + lUseTime + "毫秒");

		String enString1 = AESUtil.getInstance()
			.encrypt(getInstance().appKe2, cSrc);
		System.out.println("使用key:" + getInstance().appKe2 + "加密后的字串是:" + enString1);
		String decryptStr1 = AESUtil.getInstance()
			.decrypt(getInstance().appKe2, enString1);
		System.out.println("使用key:" + getInstance().appKe2 + "解密后的字串是:" + decryptStr1);

		// 解密
		lStart = System.currentTimeMillis();
//         String DeString = AESUtil.getInstance().decrypt("grRcs+e1YlpsxNNJlMNlfA==");
		String DeString = AESUtil.getInstance()
			.decrypt("CMrdh0tr4T9gJ8L4GGJDnoSnga9zHVC8Kbqv8hw2xTZ49PL5ZM9eaT5mu5HE3YD6JLRDJhxPF9qPi0Iqi+DjbQxV21fkLJg1K6avbPGinqOUVTYUO/ACiyVTNLcnF3yeGt6SygHwLyRGwPtLfnSLKwVPIdchYg30qOifohHAcligS2WRFfVPnlZXD7GPskpMKBKSTqcJpQIXZ8rxa31eOpTzixyzHyZ7Ys7d9rjwRwEjSNaHkER1pNARmPdtjTh2JT+JVhpWuerhCQfZFZjg5gPBwsKefSLp9DmIbydw7o9tnzzia7dElG5MmXF3XQHPTMdFnPxe+Z6KmN+T43LYN3+UQg6P7Vft6hfBYli9OmJoyxP8yxoJu55mzSAHCCkc8plLukFYjojHMLtONoDltBoyxaCQyd4zIe8nei6KYrNhMm3Sfm1bf3B/LFhs+Aca/U6GkXVIsWe1HC+46k9IshQ5gPLKKahWX+u5EgyoNwkDBVmyllgJUfs4cTtsZi3beCnz8pGt+Ec3GONKLPH0IiIlibWQqOeTUWasMHXZyC7G3l/UdeVYOVZ71ED7+Quryto1Xn2SR0tfCd3qfd32YL+B6TKaxfWlSX+Dynzv31GQiRF6+8PolrOK8Tx9YcjY8GQuCaDBwL7WIp29tP62J73XUVTuM26Nf1KdmdVC2308pfS5QIuwi/cB6jActfm8");
//         String DeString = AESUtil.getInstance().decrypt(enString);
		System.out.println("解密后的字串是：" + DeString);
		lUseTime = System.currentTimeMillis() - lStart;
		System.out.println("解密耗时：" + lUseTime + "毫秒");
	}

	/**
	 * @param str
	 * @return
	 * @desc 将16进制字符串转换为byte[]
	 */
	public static byte[] hexToBytes(String str) {
		if(str == null || "".equals(str.trim())) {
			return new byte[0];
		}

		byte[] bytes = new byte[str.length() / 2];
		for(int i = 0; i < str.length() / 2; i++) {
			String subStr = str.substring(i * 2, i * 2 + 2);
			bytes[i] = (byte) Integer.parseInt(subStr, 16);
		}

		return bytes;
	}

	public static String bytesToHexFun(byte[] bytes) {
		// 一个byte为8位，可用两个十六进制位标识
		char[] buf = new char[bytes.length * 2];
		int a = 0;
		int index = 0;
		for(byte b : bytes) { // 使用除与取余进行转换
			if(b < 0) {
				a = 256 + b;
			} else {
				a = b;
			}

			buf[index++] = HEX_CHAR[a / 16];
			buf[index++] = HEX_CHAR[a % 16];
		}

		return new String(buf);
	}

	public String getDefaultKey() {
		return appKey;
	}

	public String getDefaultIv() {
		return appIV;
	}

//	public String generateAesKey() {
//		return CommonUtils.getHexRandomStr(16);
//	}

	// 加密
	public String encrypt(String encryptStr) throws Exception {
		if(encryptStr == null) {
			return null;
		}
		byte[] encrypted = null;
		try {
			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
			byte[] raw = appKey.getBytes();
			SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
			IvParameterSpec iv = new IvParameterSpec(appIV.getBytes());// 使用CBC模式，需要一个向量iv，可增加加密算法的强度
			cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
			encrypted = cipher.doFinal(encryptStr.getBytes("utf-8"));
		} catch(NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch(NoSuchPaddingException e) {
			e.printStackTrace();
		} catch(InvalidKeyException e) {
			e.printStackTrace();
		} catch(InvalidAlgorithmParameterException e) {
			e.printStackTrace();
		} catch(IllegalBlockSizeException e) {
			e.printStackTrace();
		} catch(BadPaddingException e) {
			e.printStackTrace();
		} catch(UnsupportedEncodingException e) {
			e.printStackTrace();
		}

		return Base64Util.encode(encrypted);// 此处使用BASE64做转码。
	}

	// 解密
	public String decrypt(String decryptStr) {
		if(decryptStr == null) {
			return null;
		}
		try {
			byte[] raw = appKey.getBytes("ASCII");
			SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
			IvParameterSpec iv = new IvParameterSpec(appIV.getBytes());
			cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
			byte[] encrypted1 = Base64Util.decode(decryptStr);// 先用base64解密
			byte[] original = cipher.doFinal(encrypted1);
			String originalString = new String(original, "utf-8");
			return originalString;
		} catch(Exception ex) {
			return null;
		}
	}

	// 加密
	public String encrypt(String sKey, String encryptStr) throws Exception {
		if(encryptStr == null) {
			return null;
		}
		if(sKey == null || sKey.isEmpty()) {
			return "";
		}
		byte[] encrypted = null;
		try {
			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
			byte[] raw = sKey.getBytes();
			SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
			IvParameterSpec iv = new IvParameterSpec(appIV.getBytes());// 使用CBC模式，需要一个向量iv，可增加加密算法的强度
			cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
			encrypted = cipher.doFinal(encryptStr.getBytes("utf-8"));
		} catch(NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch(NoSuchPaddingException e) {
			e.printStackTrace();
		} catch(InvalidKeyException e) {
			e.printStackTrace();
		} catch(InvalidAlgorithmParameterException e) {
			e.printStackTrace();
		} catch(IllegalBlockSizeException e) {
			e.printStackTrace();
		} catch(BadPaddingException e) {
			e.printStackTrace();
		} catch(UnsupportedEncodingException e) {
			e.printStackTrace();
		}

		return Base64Util.encode(encrypted);// 此处使用BASE64做转码。
	}

	// 解密
	public String decrypt(String sKey, String decryptStr) {
		if(decryptStr == null) {
			return null;
		}
		try {
			byte[] raw = sKey.getBytes("ASCII");
			SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
			IvParameterSpec iv = new IvParameterSpec(appIV.getBytes());
			cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
			byte[] encrypted1 = Base64Util.decode(decryptStr);// 先用base64解密
			byte[] original = cipher.doFinal(encrypted1);
			String originalString = new String(original, "utf-8");
			return originalString;
		} catch(Exception ex) {
			return null;
		}
	}

	public String decrypt(String sSrc, String key, String ivs) throws Exception {
		try {
			byte[] raw = key.getBytes("ASCII");
			SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
			IvParameterSpec iv = new IvParameterSpec(ivs.getBytes());
			cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
			byte[] encrypted1 = Base64Util.decode(sSrc);// 先用base64解密
			byte[] original = cipher.doFinal(encrypted1);
			String originalString = new String(original, "utf-8");
			return originalString;
		} catch(Exception ex) {
			return null;
		}
	}

	public String encrypt(String encryptStr, String sKey, String ivParameter) throws Exception {
		if(encryptStr == null) {
			return null;
		}
		byte[] encrypted = null;
		try {
			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
			byte[] raw = sKey.getBytes();
			SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
			IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes());// 使用CBC模式，需要一个向量iv，可增加加密算法的强度
			cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
			encrypted = cipher.doFinal(encryptStr.getBytes("utf-8"));
		} catch(NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch(NoSuchPaddingException e) {
			e.printStackTrace();
		} catch(InvalidKeyException e) {
			e.printStackTrace();
		} catch(InvalidAlgorithmParameterException e) {
			e.printStackTrace();
		} catch(IllegalBlockSizeException e) {
			e.printStackTrace();
		} catch(BadPaddingException e) {
			e.printStackTrace();
		} catch(UnsupportedEncodingException e) {
			e.printStackTrace();
		}

		return Base64Util.encode(encrypted);// 此处使用BASE64做转码。
	}

	/**
	 * @param sSrc
	 * @return
	 * @throws Exception
	 * @desc 16进制字符串解密
	 */
	public String decryptHex(String sSrc)
		throws Exception {
		try {
			byte[] raw = appKey.getBytes("ASCII");
			SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
			IvParameterSpec iv = new IvParameterSpec(appIV.getBytes());
			cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
			byte[] encrypted1 = hexToBytes(sSrc);// 先用十六进制解码
			byte[] original = cipher.doFinal(encrypted1);
			String originalString = new String(original, "utf-8");
			return originalString;
		} catch(Exception ex) {

			return null;
		}
	}

	/**
	 * @param encData
	 * @return
	 * @throws Exception
	 * @desc 加密之后转换为16进制
	 */
	public String hexEncrypt(String encData) {
		if(appKey == null) {
			return null;
		}
		if(appKey.length() != 16) {
			return null;
		}

		try {
			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
			byte[] raw = appKey.getBytes();
			SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
			IvParameterSpec iv = new IvParameterSpec(appIV.getBytes());// 使用CBC模式，需要一个向量iv，可增加加密算法的强度
			cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
			byte[] encrypted = cipher.doFinal(encData.getBytes("utf-8"));
			return bytesToHexFun(encrypted);// 此处使用十六进制做转码。
		} catch(Exception e) {
			e.printStackTrace();
		}
		return null;
	}
}